package com.ztesoft.book.core.quartz.schedulejob.util;

import com.ztesoft.book.common.enums.ScheduleStatusEnum;
import com.ztesoft.book.common.utils.exception.ServiceException;
import com.ztesoft.book.core.quartz.schedulejob.dto.ScheduleJobDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.triggers.CronTriggerImpl;

import java.text.ParseException;
import java.util.Date;

/**
 * @Description: Quartz定时调度
 * @Author: YangFan
 * @Date: 2021-03-18 16:55
 */
@Slf4j
public final class ScheduleUtils {

    private static final String JOB_NAME = "TASK_";

    private ScheduleUtils() {
    }

    /**
     * 方法功能描述:
     * < 获取触发器Key >
     *
     * @param: 【参数1】 jobId
     * @Return: TriggerKey
     * @Author: YangFan
     * @Date: 2021-03-18 21:00
     */
    public static TriggerKey getTriggerKey(String jobId) {
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }


    /**
     * 方法功能描述:
     * < 获取JobKey >
     *
     * @param: 【参数1】 jobId
     * @Return: JobKey
     * @Author: YangFan
     * @Date: 2021-03-18 20:59
     */
    public static JobKey getJobKey(String jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }


    /**
     * 方法功能描述:
     * < 获取表达式触发器 >
     *
     * @param: 【参数1】 scheduler
     * @param: 【参数2】 jobId
     * @Return: CronTrigger
     * @Author: YangFan
     * @Date: 2021-03-18 20:59
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, String jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        }
        catch (SchedulerException e) {
            throw new ServiceException("获取定时任务CronTrigger出现异常", e);
        }
    }


    /**
     * 方法功能描述:
     * < 创建定时任务 >
     *
     * @param: 【参数1】 scheduler
     * @param: 【参数2】 scheduleJob
     * @Return: void
     * @Author: YangFan
     * @Date: 2021-03-18 20:59
     */
    public static void createJob(Scheduler scheduler, ScheduleJobDTO scheduleJobDto) {
        // 针对多节点同时构建可能会造成创建已重复的定时任务导致报错情况处理
        deleteScheduleJob(scheduler, scheduleJobDto.getId());
        try {
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJobBean.class).withIdentity(getJobKey(scheduleJobDto.getId())).build();

            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJobDto.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJobDto.getId())).withSchedule(scheduleBuilder).build();

            // 放入参数，运行时的方法可以获取
            jobDetail.getJobDataMap().put(ScheduleJobDTO.JOB_PARAM_KEY, scheduleJobDto);

            scheduler.scheduleJob(jobDetail, trigger);

            // 暂停任务
            if (scheduleJobDto.getStatus() == ScheduleStatusEnum.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJobDto.getId());
            }
        }
        catch (SchedulerException e) {
            throw new ServiceException("创建定时任务失败", e);
        }
    }


    /**
     * 方法功能描述:
     * < 更新定时任务 >
     *
     * @param: 【参数1】 scheduler
     * @param: 【参数2】 scheduleJob
     * @Return: void
     * @Author: YangFan
     * @Date: 2021-03-18 20:59
     */
    public static void updateJob(Scheduler scheduler, ScheduleJobDTO scheduleJobDto) {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJobDto.getId());

            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJobDto.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(scheduler, scheduleJobDto.getId());

            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            // 参数
            trigger.getJobDataMap().put(ScheduleJobDTO.JOB_PARAM_KEY, scheduleJobDto);

            scheduler.rescheduleJob(triggerKey, trigger);

            // 暂停任务
            if (scheduleJobDto.getStatus() == ScheduleStatusEnum.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJobDto.getId());
            }

        }
        catch (SchedulerException e) {
            throw new ServiceException("更新定时任务失败", e);
        }
    }


    /**
     * 方法功能描述:
     * < 暂停任务 >
     *
     * @param: 【参数1】 scheduler
     * @param: 【参数2】 jobId
     * @Return: void
     * @Author: YangFan
     * @Date: 2021-03-18 20:59
     */
    public static void pauseJob(Scheduler scheduler, String jobId) {
        try {
            scheduler.pauseJob(getJobKey(jobId));
        }
        catch (SchedulerException e) {
            throw new ServiceException("暂停定时任务失败", e);
        }
    }


    /**
     * 方法功能描述:
     * < 恢复任务 >
     *
     * @param: 【参数1】 scheduler
     * @param: 【参数2】 jobId
     * @Return: void
     * @Author: YangFan
     * @Date: 2021-03-18 20:58
     */
    public static void resumeJob(Scheduler scheduler, String jobId) {
        try {
            scheduler.resumeJob(getJobKey(jobId));
        }
        catch (SchedulerException e) {
            throw new ServiceException("暂停定时任务失败", e);
        }
    }


    /**
     * 方法功能描述:
     * < 删除定时任务 >
     *
     * @param: 【参数1】 scheduler
     * @param: 【参数2】 jobId
     * @Return: void
     * @Author: YangFan
     * @Date: 2021-03-18 20:58
     */
    public static void deleteJob(Scheduler scheduler, String jobId) {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        }
        catch (SchedulerException e) {
            throw new ServiceException("删除定时任务失败", e);
        }
    }



    /**
     * 方法功能描述:
     * < 删除已存在的定时任务 >
     *
     * @param scheduler 参数说明
     * @param jobId     参数说明
     * @return void
     * @author DeathCat
     * @date 2022/4/21 11:02
     */
    public static void deleteScheduleJob(Scheduler scheduler, String jobId) {
        try {
            if (scheduler.checkExists(getJobKey(jobId))) {
                scheduler.deleteJob(getJobKey(jobId));
            }
        }
        catch (SchedulerException e) {
            log.error("创建任务时校验已存在的任务，如果存在则删除，操作失败：", e);
        }
    }



    /**
     * 方法功能描述:
     * < 立即执行任务 >
     *
     * @param: 【参数1】 scheduler
     * @param: 【参数2】 scheduleJob
     * @Return: void
     * @Author: YangFan
     * @Date: 2021-03-18 20:58
     */
    public static void run(Scheduler scheduler, ScheduleJobDTO scheduleJobDto) {
        try {
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(ScheduleJobDTO.JOB_PARAM_KEY, scheduleJobDto);

            scheduler.triggerJob(getJobKey(scheduleJobDto.getId()), dataMap);
        }
        catch (SchedulerException e) {
            throw new ServiceException("立即执行定时任务失败", e);
        }
    }

    /**
     * 方法功能描述:
     * < 校验cron表达式是否能执行 >
     *
     * @param cron 参数说明
     * @return boolean
     * @author YangFan
     * @date 2021-09-09 20:04
     */
    public static boolean isValidateCanDoExpression(String cron) {
        //先校验cron表达式格式是否正确
        if (!isValidExpression(cron)) {
            return false;
        }
        CronTriggerImpl triggerImpl = new CronTriggerImpl();
        try {
            triggerImpl.setCronExpression(cron);
        }
        catch (ParseException e) {
            // TODO Auto-generated catch block
            log.info("定时任务cron校验【{}】,过期无法执行", cron);
            return false;
        }
        Date date = triggerImpl.computeFirstFireTime(null);
        return date != null && date.after(new Date());
    }

    /**
     * 方法功能描述:
     * < 校验cron表达式格式 >
     *
     * @param cron 参数说明
     * @return boolean
     * @author YangFan
     * @date 2021-09-09 20:04
     */
    public static boolean isValidExpression(String cron) {
        if (StringUtils.isEmpty(cron)) {
            return false;
        }
        return CronExpression.isValidExpression(cron);
    }

}
